using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

public class AssetBundleManager : MonoSingleton<AssetBundleManager>
{

    // 资源包缓存字典
    private Dictionary<string, AssetBundle> loadedBundle = new Dictionary<string, AssetBundle>();
    private AssetBundleManifest manifest;

    // 注意！这个名称会更具打包路径修改，可能经常需要改变
    // 资源包依赖配置名称
    private string manifestName = "AssetBundles";

    protected override void OnInit()
    {
        // 加载主资源包获取依赖信息
        LoadManifest();
    }

    #region 核心加载方法
    // 同步加载资源包
    public AssetBundle LoadBundle(string bundleName)
    {
        if (loadedBundle.ContainsKey(bundleName))
        {
            return loadedBundle[bundleName];
        }

        string path = GetBundlePath(bundleName);
        if (!File.Exists(path))
        {
            Debug.LogError($"AssetBundle not found: {path}");
            return null;
        }

        // 加载依赖
        LoadDependenciesSync(bundleName);

        AssetBundle bundle = AssetBundle.LoadFromFile(path);
        loadedBundle.Add(bundleName, bundle);
        return bundle;
    }

    // 异步加载资源包
    public IEnumerator LoadBundleAsync(string bundleName, System.Action<AssetBundle> callback)
    {
        if (loadedBundle.ContainsKey(bundleName))
        {
            callback?.Invoke(loadedBundle[bundleName]);
            yield break;
        }

        string path = GetBundlePath(bundleName);
        if (!File.Exists(path))
        {
            Debug.LogError($"AssetBundle not found: {path}");
            callback?.Invoke(null);
            yield break;
        }

        // 异步加载依赖
        yield return LoadDependenciesAsync(bundleName);

        AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path);
        yield return request;

        AssetBundle bundle = request.assetBundle;
        loadedBundle.Add(bundleName, bundle);
        callback?.Invoke(bundle);
    }

    // 同步加载资源
    public T LoadAsset<T>(string bundleName, string assetName) where T : Object
    {
        AssetBundle bundle = LoadBundle(bundleName);
        return bundle?.LoadAsset<T>(assetName);
    }

    // 异步加载资源
    public IEnumerator LoadAssetAsync<T>(string bundleName, string assetName, System.Action<T> callback) where T : Object
    {
        AssetBundle bundle = null;
        yield return LoadBundleAsync(bundleName, ab => bundle = ab);

        if (bundle != null)
        {
            AssetBundleRequest request = bundle.LoadAssetAsync<T>(assetName);
            yield return request;
            callback?.Invoke(request.asset as T);
        }
    }
    #endregion

    #region 依赖管理
    private void LoadManifest()
    {
        AssetBundle manifestBundle = AssetBundle.LoadFromFile(GetBundlePath(manifestName));
        manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        manifestBundle.Unload(false);
    }

    private void LoadDependenciesSync(string bundleName)
    {
        string[] dependencies = manifest.GetAllDependencies(bundleName);
        
        // 递归加载依赖
        foreach (string dep in dependencies)
        {
            if (!loadedBundle.ContainsKey(dep))
            {
                LoadBundle(dep);
            }
        }
    }

    private IEnumerator LoadDependenciesAsync(string bundleName)
    {
        string[] dependencies = manifest.GetAllDependencies(bundleName);
        foreach (string dep in dependencies)
        {
            if (!loadedBundle.ContainsKey(dep))
            {
                yield return LoadBundleAsync(dep, null);
            }
        }
    }
    #endregion

    #region 工具方法
    private string GetBundlePath(string bundleName)
    {
        // 根据平台选择路径
        return Path.Combine(Consts.AssetBundlesPath, bundleName);
    }

    public void UnloadBundle(string bundleName, bool unloadAllLoadedObjects = false)
    {
        if (loadedBundle.TryGetValue(bundleName, out AssetBundle bundle))
        {
            bundle.Unload(unloadAllLoadedObjects);
            loadedBundle.Remove(bundleName);
        }
    }

    public void UnloadAll(bool unloadAllLoadedObjects = false)
    {
        foreach (var bundle in loadedBundle.Values)
        {
            bundle.Unload(unloadAllLoadedObjects);
        }
        loadedBundle.Clear();
    }
    #endregion
}